﻿using href.Utils;
using MicroMvvm;
using ModernUILogViewer.Business;
using ModernUILogViewer.Model;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows.Input;

namespace ModernUILogViewer.ViewModel
{
    public class TestFileResultViewModel : BaseViewModel
    {
        #region Constructor

        public TestFileResultViewModel(MainViewModel mainVM)
        {
            optionsVM = mainVM.OptionsVM;
        }

        #endregion

        #region Fields

        private OptionsViewModel optionsVM;

        public event EventHandler EventLinePatternException;
        public event EventHandler EventDatePatternException;
        public event EventHandler EventLevelPatternException;

        #endregion

        #region Properties

        private readonly IDictionary<string, string> dateFormatList = new Dictionary<string, string>
        {
            {"d", "d - The day of the month, from 1 through 31"},
            {"dd", "dd - The day of the month, from 01 through 31"},
            {"ddd", "ddd - The abbreviated name of the day of the week"},
            {"dddd", "dddd - The full name of the day of the week"},            
            {"f", "f- The tenths of a second in a date and time value"},
            {"ff", "ff - The hundredths of a second in a date and time value"},
            {"fff", "fff - The milliseconds in a date and time value"},
            {"h", "h - The hour, using a 12-hour clock from 1 to 12"},
            {"hh", "hh - The hour, using a 12-hour clock from 01 to 12"},
            {"H", "H - The hour, using a 24-hour clock from 0 to 23"},
            {"HH", "HH - The hour, using a 24-hour clock from 00 to 23"},
            {"m", "m - The minute, from 0 through 59"},
            {"mm", "mm - The minute, from 00 through 59"},
            {"M", "M - The month, from 1 through 12"},
            {"MM", "MM - The month, from 01 through 12"},
            {"MMM", "MMM - The abbreviated name of the month"},
            {"MMMM", "MMMM - The full name of the month"},
            {"s", "s - The second, from 0 through 59"},
            {"ss", "ss - The second, from 00 through 59"},
            {"t", "t - The first character of the AM/PM designator"},
            {"tt", "tt - The AM/PM designator"},
            {"y", "y - The year, from 0 to 99"},
            {"yy", "yy - The year, from 00 to 99"},
            {"yyy", "yyy - The year, with a minimum of three digits"},
            {"yyyy", "yyyy - The year as a four-digit number"},
            {"yyyyy", "yyyyy - The year as a five-digit number"},

        };

        private CultureInfo selectedCultureInfo;

        public CultureInfo SelectedCultureInfo
        {
            get { return selectedCultureInfo; }
            set
            {
                selectedCultureInfo = value;
                RaisePropertyChanged(() => SelectedCultureInfo);
            }
        }


        public IDictionary<string, string> DateFormatList
        {
            get { return dateFormatList; }
        }

        private string parseResult;

        public string ParseResult
        {
            get { return parseResult; }
            set
            {
                parseResult = value;
                RaisePropertyChanged(() => ParseResult);
            }
        }

        private string inputRegex;

        public string InputRegex
        {
            get { return inputRegex; }
            set
            {
                inputRegex = value;
                RaisePropertyChanged(() => InputRegex);
            }
        }

        private string inputDate;

        public string InputDate
        {
            get { return inputDate; }
            set
            {
                inputDate = value;
                RaisePropertyChanged(() => InputDate);
            }
        }

        //private string selectedDateFormat;

        //public string SelectedDateFormat
        //{
        //    get { return selectedDateFormat; }
        //    set
        //    {
        //        selectedDateFormat = value;
        //        RaisePropertyChanged(() => SelectedDateFormat);
        //    }
        //}

        private string dateFormat;

        public string DateFormat
        {
            get { return dateFormat; }
            set
            {
                dateFormat = value;
                RaisePropertyChanged(() => DateFormat);
            }
        }


        //private string currentTestFile;

        //public string CurrentTestFile
        //{
        //    get { return currentTestFile; }
        //    set
        //    {
        //        currentTestFile = value;
        //        RaisePropertyChanged(() => CurrentTestFile);
        //    }
        //}


        private int entriesCount;

        public int EntriesCount
        {
            get { return entriesCount; }
            set
            {
                entriesCount = value;
                RaisePropertyChanged(() => EntriesCount);
            }
        }

        private string dateTestResult;

        public string DateTestResult
        {
            get { return dateTestResult; }
            set
            {
                dateTestResult = value;
                RaisePropertyChanged(() => DateTestResult);
            }
        }

        private string logLevelTestResult;

        public string LogLevelTestResult
        {
            get { return logLevelTestResult; }
            set
            {
                logLevelTestResult = value;
                RaisePropertyChanged(() => LogLevelTestResult);
            }
        }

        private string regexPattern;

        public string RegexPattern
        {
            get { return regexPattern; }
            set
            {
                regexPattern = value;
                RaisePropertyChanged(() => RegexPattern);
            }
        }

        private string resultRegex;

        public string ResultRegex
        {
            get { return resultRegex; }
            set
            {
                resultRegex = value;
                RaisePropertyChanged(() => ResultRegex);
            }
        }

        #endregion

        #region Methods

        private bool CheckRegexValidation()
        {
            string patternLine = optionsVM.LineBeginningRegExp;
            string patternDatetime = optionsVM.DateRegExp;
            string patternLevel = optionsVM.LevelRegExp;

            bool canOpen = true;

            Func<string, bool> func = (str) =>
            {
                return (canOpen = Utility.IsValidRegex(str));
            };

            if (func(patternLine) == false)
                RaiseEventInvoker(EventLinePatternException);
            else if (func(patternDatetime) == false)
                RaiseEventInvoker(EventDatePatternException);
            else if (func(patternLevel) == false)
                RaiseEventInvoker(EventLevelPatternException);

            return canOpen;
        }

        private void TestContent()
        {
            string patternLine = optionsVM.LineBeginningRegExp;
            string patternDatetime = optionsVM.DateRegExp;
            string patternLevel = optionsVM.LevelRegExp;
            string formatDatetime = optionsVM.DateFormat;
            int groupMatchDatetime = optionsVM.DateGroupMatch;
            int groupMatchLevel = optionsVM.LevelGroupMatch;
            bool matchDate = optionsVM.IncludeDateMatch;
            bool matchLogLevel = optionsVM.IncludeLevelMatch;

            //currentTestFile = filename;

            byte[] byteArray = Encoding.UTF8.GetBytes(inputRegex);
            //byte[] byteArray = Encoding.ASCII.GetBytes(contents);
            MemoryStream stream = new MemoryStream(byteArray);

            // convert stream to string
            StreamReader sr = new StreamReader(stream);

            //System.IO.StreamReader sr = new System.IO.StreamReader(filename, Encoding.UTF8);

            string line;

            bool dateParsingErrorFound = false;
            bool logLevelParsingErrorFound = false;

            int dateEntriesCount = 0;
            int logLevelEntriesCount = 0;
            entriesCount = 0;

            StringBuilder sb = new StringBuilder();

            while (((line = sr.ReadLine()) != null))
            {
                sb.Append(line);
                sb.Append(Environment.NewLine);

                Match match = Regex.Match(line, patternLine);

                if (match.Success)
                {
                    entriesCount++;

                    if (matchDate)
                    {
                        match = Regex.Match(line, patternDatetime);

                        if (match.Success && dateParsingErrorFound == false)
                        {
                            try
                            {
                                DateTime.ParseExact(match.Groups[groupMatchDatetime].Value, formatDatetime, CultureInfo.InvariantCulture);
                                dateEntriesCount++;
                            }
                            catch (Exception)
                            {
                                dateParsingErrorFound = true;
                            }
                        }
                    }

                    if (matchLogLevel)
                    {
                        match = Regex.Match(line, patternLevel);

                        if (match.Success && logLevelParsingErrorFound == false)
                        {
                            try
                            {
                                //bool a = Enum.TryParse<LOG_LEVEL>(typeof(LOG_LEVEL), match.Groups[groupMatchLevel].Value);
                                LOG_LEVEL level;
                                logLevelParsingErrorFound = Enum.TryParse<LOG_LEVEL>(match.Groups[groupMatchLevel].Value, out level) == false;
                                logLevelEntriesCount++;
                            }
                            catch (Exception)
                            {

                            }
                        }
                    }
                }
            }

            sr.Close();

            if (matchDate)
            {
                if (dateParsingErrorFound)
                    dateTestResult = "parsing error";
                else
                    dateTestResult = dateEntriesCount.ToString(); // String.Format("{0} entries found", dateEntriesCount);
            }
            else
                dateTestResult = "not included";

            if (matchLogLevel)
            {
                if (logLevelParsingErrorFound)
                    logLevelTestResult = "parsing error";
                else
                    logLevelTestResult = logLevelEntriesCount.ToString(); // String.Format("{0} entries found", logLevelEntriesCount);
            }
            else
                logLevelTestResult = "not included";

            inputRegex = sb.ToString();

            //RaisePropertyChanged(() => CurrentTestFile);

            RaisePropertyChanged(() => EntriesCount);
            RaisePropertyChanged(() => DateTestResult);
            RaisePropertyChanged(() => LogLevelTestResult);
            RaisePropertyChanged(() => InputRegex);

            sb = new StringBuilder();
            sb.Append("Records: ");
            sb.Append(entriesCount);
            sb.Append(Environment.NewLine);
            sb.Append("Date: ");
            sb.Append(dateTestResult);
            sb.Append(Environment.NewLine);
            sb.Append("Level: ");
            sb.Append(logLevelTestResult);
            sb.Append(Environment.NewLine);

            ResultRegex = sb.ToString();
        }

        void OpenTestFileExecute()
        {

            string[] files = ShowOpenFilesDlg(false);

            string filename = files.Length > 0 ? files[0] : null;

            if (filename != null)
            {
                StringBuilder sb = new StringBuilder();

                using (StreamReader sr = EncodingTools.OpenTextFile(filename))
                {                    
                    int linecounter = 50;
                    string line;

                    while (((line = sr.ReadLine()) != null) && linecounter-- > 0)
                    {
                        sb.Append(line);
                        sb.Append(Environment.NewLine);
                    }
                }

                InputRegex = sb.ToString();

                TestContent();
            }
        }

        void ProcessRegexExecute()
        {
            StringBuilder sb = new StringBuilder();

            if (String.IsNullOrEmpty(inputRegex) == false && String.IsNullOrEmpty(regexPattern) == false)
            {
                if (Utility.IsValidRegex(regexPattern))
                {
                    MatchCollection matches = Regex.Matches(inputRegex, regexPattern);

                    foreach (Match match in matches)
                    {
                        int idx = 0;

                        foreach (Group group in match.Groups)
                        {
                            sb.Append("Group ");
                            sb.Append(idx);
                            sb.Append(" Index ");
                            sb.Append(group.Index);
                            sb.Append(" Length ");
                            sb.Append(group.Length);
                            sb.Append(" Value: ");
                            sb.Append(group.Value);
                            sb.Append(Environment.NewLine);

                            idx++;
                        }
                    }

                    if (sb.Length > 0)
                        resultRegex = sb.ToString();
                    else
                        resultRegex = "No matches";
                }
                else
                    resultRegex = "regex not valid";
            }

            RaisePropertyChanged(() => ResultRegex);
        }

        void ProcessContentExecute()
        {
            TestContent();
        }

        void ParseDateExecute()
        {
            if (String.IsNullOrEmpty(dateFormat) == false && String.IsNullOrEmpty(inputDate) == false)
            {
                DateTime dt;

                bool isValidDate = DateTime.TryParseExact(inputDate, dateFormat, SelectedCultureInfo, DateTimeStyles.None, out dt);

                if (isValidDate)
                {
                    ParseResult = dt.ToString(dateFormat);
                }
                else
                {
                    ParseResult = "No valid date or format";
                }
            }
        }

        void AddToDateFormatExecute(string param)
        {
            StringBuilder sb = new StringBuilder(dateFormat);
            sb.Append(param);

            DateFormat = sb.ToString();
        }

        void InitVMExecute()
        {
            SelectedCultureInfo = optionsVM.SelectedCultureInfo;
        }


        #endregion

        #region Commands

        public ICommand OpenTestFile { get { return new MvvmFoundation.Wpf.RelayCommand(OpenTestFileExecute); } }
        public ICommand ProcessRegex { get { return new MvvmFoundation.Wpf.RelayCommand(ProcessRegexExecute); } }
        public ICommand ProcessContent { get { return new MvvmFoundation.Wpf.RelayCommand(ProcessContentExecute); } }
        public ICommand ParseDate { get { return new MvvmFoundation.Wpf.RelayCommand(ParseDateExecute); } }
        public ICommand AddToDateFormat { get { return new MvvmFoundation.Wpf.RelayCommand<string>(AddToDateFormatExecute); } }
        public ICommand InitVM { get { return new MvvmFoundation.Wpf.RelayCommand(InitVMExecute); } }

        #endregion
    }
}
